/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.controller.evaluation.prepare;

import cn.ac.nercis.pes.common.constant.evaluation.EvaluationModule;
import cn.ac.nercis.pes.common.constant.project.FinishState;
import cn.ac.nercis.pes.common.constant.project.SystemState;
import cn.ac.nercis.pes.community.component.controls.TreeListCell;
import cn.ac.nercis.pes.community.component.dialog.MessageDialog;
import cn.ac.nercis.pes.community.component.dialog.MessageType;
import cn.ac.nercis.pes.community.component.dialog.PageDialog;
import cn.ac.nercis.pes.community.component.dialog.SpinnerDialog;
import cn.ac.nercis.pes.community.controller.base.InitEvalDataLoad;
import cn.ac.nercis.pes.community.model.vo.MenuVO;
import cn.ac.nercis.pes.community.service.SysConfigService;
import cn.ac.nercis.pes.community.support.AbstractFxmlView;
import cn.ac.nercis.pes.community.utils.*;
import cn.ac.nercis.pes.community.views.evaluation.prepare.*;
import cn.ac.nercis.pes.community.views.evaluation.prepare.asset.*;
import cn.ac.nercis.pes.community.views.evaluation.prepare.course.EvalMemberView;
import cn.ac.nercis.pes.community.views.evaluation.prepare.course.EvalProcessView;
import cn.ac.nercis.pes.community.views.evaluation.prepare.tools.EvalPointView;
import cn.ac.nercis.pes.community.views.evaluation.prepare.tools.EvalToolView;
import cn.ac.nercis.pes.model.dto.project.SystemProcessDTO;
import cn.ac.nercis.pes.service.evaluation.AssetExportService;
import cn.ac.nercis.pes.service.project.SystemProcessService;
import cn.ac.nercis.pes.service.utils.FileManage;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;

/**
 * 测评准备管理控制器
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
public class PrepareController implements Initializable {

    @FXML
    private StackPane page;
    @FXML
    private BorderPane bodyPane;

    @FXML
    private TreeView<MenuVO> menuTreeView;

    private final SystemProcessService processService;
    private final AssetExportService assetExportService;
    private final BooleanProperty enablePaste = new SimpleBooleanProperty(false);
    private final BooleanProperty assetContextMenu = new SimpleBooleanProperty(false);
    private final ObjectProperty<MenuVO> currentMenu = new SimpleObjectProperty<>();

    private TreeItem<MenuVO> rootMenu;
    private String currentSystemId;

    public PrepareController() {
        this.processService = SpringUtils.getBean(SystemProcessService.class);
        this.assetExportService = SpringUtils.getBean(AssetExportService.class);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        rootMenu = new TreeItem<>(new MenuVO(0,"","测评准备菜单数据",""));
        this.initMenuControl();
    }

    public void initializeData(String systemId){
        this.currentSystemId = systemId;
        this.initMenuData();
        this.refreshEvalProcess();
    }

    public StackPane getPage(){
        return this.page;
    }

    public void refreshEvalProcess(){
        initMenuItemEvalState(processService.findSystemProcess(this.currentSystemId, SystemState.PREPARE));
    }

    public void setEvalProcess(EvaluationModule menu, FinishState state){
        var process = SystemProcessDTO.builder()
                .systemId(this.currentSystemId)
                .state(SystemState.PREPARE)
                .name(menu.getName())
                .finishState(state)
                .build();
        CompletableFuture.supplyAsync(() -> processService.setSystemProcess(process))
                .thenAccept(res -> Platform.runLater(() -> {
                    if (!res.isSuccess()) {
                        MessageDialog.builder()
                                .withType(MessageType.WARN)
                                .withMessage("记录测评进度失败")
                                .withContainer(page)
                                .build().show();
                    }else{
                        this.refreshEvalProcess();
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("保存系统测评进度出错!", ex);
                    return null;
                });
    }

    private void initMenuControl() {
        menuTreeView.setContextMenu(this.generatePrepareContextMenu());
        menuTreeView.setOnContextMenuRequested(eve -> this.onPrepareContextMenuRequested());
        menuTreeView.setCellFactory(tree -> new TreeListCell(10, true));
        menuTreeView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (Objects.nonNull(newValue) && Objects.nonNull(newValue.getValue())) {
                loadPageContent(newValue.getValue());
            }
        });
        menuTreeView.setRoot(rootMenu);
        menuTreeView.setShowRoot(false);
    }

    private void initMenuData(){
        rootMenu.getChildren().clear();
        EvaluationModule.findPrepareModules()
                .forEach(em->{
                    var menuItem = new MenuVO(1,"",em.getName(),"none-process-icon");
                    TreeItem<MenuVO> assetMenus = new TreeItem<>(menuItem);
                    List<EvaluationModule> subItems = null;
                    if(em == EvaluationModule.COURSE){
                        subItems=EvaluationModule.findCourseModules();
                    }else if(em == EvaluationModule.SYSTEM_ASSET){
                        subItems = EvaluationModule.findSystemAssetModules();
                    }else if(em == EvaluationModule.METHOD_TOOL){
                        subItems = EvaluationModule.findMethodToolModules();
                    }
                    if(CollectionUtils.isNotEmpty(subItems)){
                        for(var subEm : subItems){
                            var subMenuItem = new MenuVO(2,em.getName(),subEm.getName(),"none-process-icon");
                            assetMenus.getChildren().add(new TreeItem<>(subMenuItem));
                        }
                    }
                    rootMenu.getChildren().add(assetMenus);
                });
        menuTreeView.getSelectionModel().select(0);
    }

    private void initMenuItemEvalState(List<SystemProcessDTO> systemProcess){
        rootMenu.getChildren().forEach(item->{
            var menuItem = item.getValue();
            if(StringUtils.equals(menuItem.getTitle(),EvaluationModule.COURSE.getName())){
                this.initSubMenuItemEvalState(item,systemProcess,EvaluationModule.findCourseModules());
            } else if(StringUtils.equals(menuItem.getTitle(),EvaluationModule.SYSTEM_ASSET.getName())){
                this.initSubMenuItemEvalState(item,systemProcess,EvaluationModule.findSystemAssetModules());
            } else if(StringUtils.equals(menuItem.getTitle(),EvaluationModule.METHOD_TOOL.getName())){
                this.initSubMenuItemEvalState(item,systemProcess,EvaluationModule.findMethodToolModules());
            } else {
                var state = systemProcess.stream()
                        .filter(sp-> StringUtils.equals(menuItem.getTitle(),sp.getName()))
                        .map(SystemProcessDTO::getFinishState)
                        .findFirst()
                        .orElse(FinishState.NONE);
                menuItem.setIcon(ControlUtils.finishStateToIcon(state));
                menuItem.setState(state);
            }
        });
        menuTreeView.refresh();
    }

    private void initSubMenuItemEvalState(TreeItem<MenuVO> node,List<SystemProcessDTO> systemProcess,List<EvaluationModule> subModules){
        var itemsCount = subModules.size();
        var subProcess = systemProcess.stream()
                .filter(sp->subModules.stream().anyMatch(am->StringUtils.equals(sp.getName(),am.getName())))
                .toList();
        var finishCount = subProcess.stream()
                .filter(sp->Objects.equals(sp.getFinishState(),FinishState.FINISH))
                .count();
        node.getChildren().stream()
                .filter(item->Objects.nonNull(item.getValue()))
                .forEach(assetItem->{
                    var state = subProcess.stream()
                            .filter(sp-> StringUtils.equals(assetItem.getValue().getTitle(),sp.getName()))
                            .map(SystemProcessDTO::getFinishState)
                            .findFirst()
                            .orElse(FinishState.NONE);
                    assetItem.getValue().setIcon(ControlUtils.finishStateToIcon(state));
                    assetItem.getValue().setState(state);
                });
        if(CollectionUtils.isEmpty(subProcess)){
            node.getValue().setIcon(ControlUtils.finishStateToIcon(FinishState.NONE));
            node.getValue().setState(FinishState.NONE);
        }else{
            var state = itemsCount == finishCount ? FinishState.FINISH : FinishState.INCOMPLETE;
            node.getValue().setIcon(ControlUtils.finishStateToIcon(state));
            node.getValue().setState(state);
        }
    }

    private void loadPageContent(MenuVO menuVO){
        if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.EVALUATION_INF_SYSTEM.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, EvaluationInfSystemView.class)));
        } else if (StringUtils.equals(menuVO.getTitle(),EvaluationModule.EVALUATION_BASIS.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, EvaluationBasisView.class)));
        }  else if (StringUtils.equals(menuVO.getTitle(),EvaluationModule.BUSINESS.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, BusinessView.class)));
        } else if (StringUtils.equals(menuVO.getTitle(),EvaluationModule.NETWORK_TOPOLOGY.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, NetworkTopologyView.class)));
        } else if (StringUtils.equals(menuVO.getTitle(),EvaluationModule.PASSWORD_USE.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, PasswordUseView.class)));
        } else if (StringUtils.equals(menuVO.getTitle(),EvaluationModule.PASSWORD_SERVICE.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, PasswordServiceView.class)));
        } else if (StringUtils.equals(menuVO.getTitle(),EvaluationModule.SECURITY_THREAT.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, SecurityThreatView.class)));
        } else if (StringUtils.equals(menuVO.getTitle(),EvaluationModule.LAST_EVALUATION.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, LastEvaluationView.class)));
        } else if(StringUtils.equals(menuVO.getCode(),EvaluationModule.COURSE.getName())){
            loadCoursePageContent(menuVO);
        } else if(StringUtils.equals(menuVO.getCode(),EvaluationModule.SYSTEM_ASSET.getName())){
            loadAssetManagePageContent(menuVO);
        } else if(StringUtils.equals(menuVO.getCode(),EvaluationModule.METHOD_TOOL.getName())){
            loadMethodToolPageContent(menuVO);
        }
    }

    private void loadCoursePageContent(MenuVO menuVO){
        if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.COURSE_PROCESS.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, EvalProcessView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.COURSE_MEMBER.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, EvalMemberView.class)));
        }
    }

    private void loadAssetManagePageContent(MenuVO menuVO){
        if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_PHY_ENV.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, PhyEnvView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_PHY_SECURITY.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, PhySecurityView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_PWD_PRODUCT.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, PwdProductView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_SERVER_STORAGE.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, ServerStorageView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_NETWORK_SECURITY.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, NetworkSecurityView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_DATABASE.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, DatabaseView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_KEY_BUSINESS.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, KeyBusinessView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_KEY_DATA.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, KeyDataView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_COMMUNICATION_CHANNEL.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, ChannelView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_SECURITY_DOC.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, SecurityDocView.class)));
        } else if(StringUtils.equals(menuVO.getTitle(),EvaluationModule.ASSET_PERSONNEL.getName())){
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, PersonnelView.class)));
        }
    }

    private void loadMethodToolPageContent(MenuVO menuVO) {
        if (StringUtils.equals(menuVO.getTitle(), EvaluationModule.METHOD_TOOL_TOOLS.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, EvalToolView.class)));
        } else if (StringUtils.equals(menuVO.getTitle(), EvaluationModule.METHOD_TOOL_POINT.getName())) {
            this.loadPageContent(menuVO, Objects.requireNonNull(AppStore.getEvaluationView(this.currentSystemId, EvalPointView.class)));
        }
    }

    private void loadPageContent(MenuVO menuVO, @NonNull AbstractFxmlView view) {
        this.currentMenu.set(menuVO);
        bodyPane.setCenter(view.load());
        if (view.getController() instanceof InitEvalDataLoad controller) {
            controller.initializeData(this.currentSystemId);
        }
    }

    private ContextMenu generatePrepareContextMenu() {
        final ContextMenu contextMenu = new ContextMenu();
        contextMenu.getStyleClass().add("pes-context-menu");
        final MenuItem importItem = new MenuItem("导入资产", IconUtils.getGlyph("download-icon", "context-menu-icon"));
        importItem.visibleProperty().bind(assetContextMenu);
        importItem.setOnAction(e -> onImportSystemAsset());
        final MenuItem exportItem = new MenuItem("导出资产", IconUtils.getGlyph("upload-icon", "context-menu-icon"));
        exportItem.visibleProperty().bind(assetContextMenu);
        exportItem.setOnAction(e -> onExportSystemAsset());
        contextMenu.getItems().addAll(importItem, exportItem);
        return contextMenu;
    }

    private void onPrepareContextMenuRequested(){
        var menu = menuTreeView.getSelectionModel().getSelectedItem().getValue();
        // enable data copy
        this.enablePaste.set(DataCopyUtils.hasClipboard(menu.getTitle(),SystemState.PREPARE));
        // set asset menu state
        var isSystemAsset = StringUtils.equals(EvaluationModule.SYSTEM_ASSET.getName(), menu.getTitle())
                || EvaluationModule.findSystemAssetModules().stream().anyMatch(item -> StringUtils.equals(item.getName(), menu.getTitle()));
        this.assetContextMenu.set(isSystemAsset);
    }

    private void onImportSystemAsset() {
        LogUtils.info(log, "执行导入资产操作...");
        importAssetData();
    }

    private void onExportSystemAsset() {
        LogUtils.info(log, "执行导出资产操作...");
        File storageDir = FileUtils.getFile(SpringUtils.getBean(SysConfigService.class).getAssetTemplateStoragePath());
        if(!storageDir.exists()){
            MessageDialog.builder()
                    .withContainer(this.getPage())
                    .withType(MessageType.WARN)
                    .withMessage("导出系统资产存储路径不存在，请设置默认存储路径！")
                    .build().show();
            return;
        }
        var spinnerDialog = SpinnerDialog.builder()
                .withProcessTip("正在导出系统资产...")
                .withContainer(Objects.requireNonNull(this.page))
                .build();
        this.assetExportService.setProcessCallback((value, tip) -> Platform.runLater(() -> {
            spinnerDialog.getSpinner().setProgress(value);
            spinnerDialog.setProcessTip(tip);
        }));
        spinnerDialog.show();
        LogUtils.info(log, "导出系统资产...");
        LogUtils.record(log, "请求数据，systemId：{},storageDir:{}", this.currentSystemId, storageDir);
        String templateFile = FileManage.getAssetTemplateFile(AppStore.getCurrentSystem(currentSystemId).getSecurityLevel());
        CompletableFuture.supplyAsync(() -> assetExportService.exportAsset(this.currentSystemId, storageDir, templateFile))
                .thenAccept(res -> Platform.runLater(() -> {
                    spinnerDialog.close();
                    if (res.isSuccess()) {
                        MessageDialog.builder()
                                .withType(MessageType.SUCCESS).withMessage("系统资产导出成功")
                                .withContainer(Objects.requireNonNull(this.page))
                                .build().show();
                        AppUtils.openFile(res.getData());
                    } else {
                        MessageDialog.builder()
                                .withType(MessageType.FAIL).withMessage("系统资产导出失败！")
                                .withContainer(Objects.requireNonNull(this.page))
                                .build().show();
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("导出系统资产出错!", ex);
                    return null;
                });

    }

    private void importAssetData() {
        var currentSystem = AppStore.getCurrentSystem(this.currentSystemId);
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择导入的资产文档");
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Excel 文档", "*.xlsx"));
        SystemUtils.initFileChooserDirectory(fileChooser);
        File selectFile = fileChooser.showOpenDialog(Objects.requireNonNull(currentSystem).getEvaluationStage());
        if (Objects.isNull(selectFile)) {
            return;
        }
        SystemUtils.setRecentDirectory(selectFile.getParentFile());
        var importView = AppStore.getEvaluationView(this.currentSystemId, AssetImportView.class);
        PageDialog pageDialog = PageDialog.builder()
                .withIcon("upload-icon")
                .withTitle("导入资产对象")
                .withShowActions(false)
                .withContent(Objects.requireNonNull(importView).load())
                .withOwner(AppStore.getCurrentSystem(this.currentSystemId).getEvaluationStage())
                .build();
        pageDialog.show();
        if (importView.getController() instanceof AssetImportController controller) {
            controller.initializeData(this.currentSystemId, selectFile, pageDialog, () -> {
                if (Objects.nonNull(currentMenu.get()) && StringUtils.equals(currentMenu.get().getCode(), EvaluationModule.SYSTEM_ASSET.getName())) {
                    loadAssetManagePageContent(currentMenu.get());
                }
            });
        }
    }




}